/*
 *  The MIT License
 *
 *  Copyright 2010 marco.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
package com.elogiclab.springsecurity.gae.filter;

import java.io.IOException;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;

import com.google.appengine.api.users.UserServiceFactory;

public class GoogleAccountsLogoutFilter extends GenericFilterBean {

	private String filterProcessesUrl = "/j_spring_security_logout";
	private List<LogoutHandler> handlers;
	private String afterLogoutUrl = "/";

        /**
         * Filter action for the logout URL.
         *
         *
         * @param request the request
         * @param response the response
         * @param chain filter chain
         * @throws IOException if an I/O error occour
         * @throws ServletException
         */
        public void doFilterHttp(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		if (requiresLogout(request, response)) {
			Authentication auth = SecurityContextHolder.getContext()
					.getAuthentication();

			if (logger.isDebugEnabled()) {
				logger.debug("User "+auth+" is logging out");
			}

			for (LogoutHandler handler : getHandlers()) {
				handler.logout(request, response, auth);
			}

			// redirect to logout page
			String googleAccountsLogoutURL = UserServiceFactory
					.getUserService().createLogoutURL(getAfterLogoutUrl());
			response.sendRedirect(googleAccountsLogoutURL);

			return;
		}

		chain.doFilter(request, response);
	}

	/**
	 * Tests if current URL matches the logout URL.
	 * 
	 * @param request
	 *            the request
	 * @param response
	 *            the response
	 * 
	 * @return <code>true</code> if logout should occur, <code>false</code>
	 *         otherwise
	 */
	protected boolean requiresLogout(HttpServletRequest request,
			HttpServletResponse response) {
		String uri = request.getRequestURI();
		int pathParamIndex = uri.indexOf(';');

		if (pathParamIndex > 0) {
			uri = uri.substring(0, pathParamIndex);
		}
		int queryParamIndex = uri.indexOf('?');
		if (queryParamIndex > 0) {
			uri = uri.substring(0, queryParamIndex);
		}
		if ("".equals(request.getContextPath())) {
			return uri.endsWith(filterProcessesUrl);
		}
		return uri.endsWith(request.getContextPath() + filterProcessesUrl);
	}

        /**
         *
         * @param filterProcessesUrl the URL that invokes logout
         */
        public void setFilterProcessesUrl(String filterProcessesUrl) {
		Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl),
				filterProcessesUrl + " isn't a valid value for"
						+ " 'filterProcessesUrl'");
		this.filterProcessesUrl = filterProcessesUrl;
	}

        /**
         *
         * @return the URL that invokes logout
         */
        protected String getFilterProcessesUrl() {
		return filterProcessesUrl;
	}

	@Override
	public void afterPropertiesSet() {
		Assert.notEmpty(getHandlers(), "LogoutHandlers are required");
	}

	public void setHandlers(List<LogoutHandler> handlers) {
		this.handlers = handlers;
	}

	public List<LogoutHandler> getHandlers() {
		return handlers;
	}

        /**
         *
         * @param afterLogoutUrl sets the redirect URL after logout
         */
        public void setAfterLogoutUrl(String afterLogoutUrl) {
		this.afterLogoutUrl = afterLogoutUrl;
	}

        /**
         *
         * @return the redirect URL after logout
         */
        public String getAfterLogoutUrl() {
		return afterLogoutUrl;
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		doFilterHttp(httpRequest, httpResponse, chain);
	}

}
